﻿using System;
using System.Collections.Generic;


namespace advadev.DataStorage
{

    /// <summary>
    /// Abstract base class for all data storages
    /// </summary>
    abstract public class DataStorageBase : IDataStorage
    {

        /// <summary>
        /// Initializes a new instance of the DataStorageBase class by the given
        /// parameter
        /// </summary>
        /// <param name="applicationKey">
        /// Application key (must not be null or empty)
        /// </param>
        /// <param name="genericEntityDataStorage">
        /// Generic entity data storage
        /// </param>
        /// <exception cref="ArgumentException">
        /// Will be thrown if the application key is empty or null
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the generic entity data storage is null
        /// </exception>
        public DataStorageBase(string applicationKey, IGenericEntityDataStorage genericEntityDataStorage)
        {
            if (string.IsNullOrEmpty(applicationKey))
            {
                throw new ArgumentException("Invalid application key", "applicationKey");
            }
            if (genericEntityDataStorage == null)
            {
                throw new ArgumentNullException("genericEntityDataStorage");
            }
            ApplicationKey = applicationKey;
            GenericEntityDataStorage = genericEntityDataStorage;
        }


        /// <summary>
        /// Gets the application key
        /// </summary>
        public string ApplicationKey
        {
            get;
            private set;
        }


        /// <summary>
        /// Gets or sets the generic entity data storage
        /// </summary>
        private IGenericEntityDataStorage GenericEntityDataStorage
        {
            get;
            set;
        }


        /// <summary>
        /// Saves the given generic object
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        /// <param name="genericObject">
        /// Generic object to save (must not be null and must be serializable)
        /// </param>
        public void Save<T>(T genericObject) where T : IGenericObject
        {
            if (genericObject == null)
            {
                return;
            }
            GenericEntity genericEntity = new GenericEntity(genericObject, ApplicationKey);
            if (genericEntity.XmlSerialization != null)
            {
                GenericEntityDataStorage.Save(genericEntity);
            }
        }

        /// <summary>
        /// Saves the given list of generic object
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        /// <param name="genericObjects">
        /// List of generic object to save (must not be null and not contain
        /// null or not serializable generic objects)
        /// </param>
        public void Save<T>(IList<T> genericObjects) where T : IGenericObject
        {
            if (genericObjects == null || genericObjects.Count == 0)
            {
                return;
            }
            IList<GenericEntity> genericEntities = new List<GenericEntity>();
            foreach (T genericObject in genericObjects)
            {
                if (genericObject != null)
                {
                    GenericEntity genericEntity = new GenericEntity(genericObject, ApplicationKey);
                    if (genericEntity.XmlSerialization != null)
                    {
                        genericEntities.Add(genericEntity);
                    }
                }
            }
            GenericEntityDataStorage.Save(genericEntities);
        }

        /// <summary>
        /// Finds the generic object with the given id and the generic object
        /// type
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        /// <param name="id">
        /// Id of the generic object to find
        /// </param>
        /// <returns>
        /// The generic object with the given id or null
        /// </returns>
        public T Find<T>(string id) where T : IGenericObject
        {
            // check parameter
            if (string.IsNullOrEmpty(id))
            {
                return default(T);
            }

            // read from data storage
            GenericEntity genericEntity = GenericEntityDataStorage.Find(id, ApplicationKey, typeof(T).FullName);
            if (genericEntity != null)
            {
                return (T)genericEntity.CreateGenericObject(typeof(T));
            }
            return default(T);
        }

        /// <summary>
        /// Selects all generic objects of the generic object type
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        /// <returns>
        /// List of generic objects
        /// </returns>
        public IList<T> Select<T>() where T : IGenericObject
        {
            List<T> result = new List<T>();
            ICollection<GenericEntity> genericEntities = GenericEntityDataStorage.Select(ApplicationKey, typeof(T).FullName);
            if (genericEntities != null)
            {
                foreach (GenericEntity genericEntity in genericEntities)
                {
                    if (genericEntity != null)
                    {
                        T genericObject = (T)genericEntity.CreateGenericObject(typeof(T));
                        if (genericObject != null)
                        {
                            result.Add(genericObject);
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Deletes the given generic object
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        /// <param name="genericObject">
        /// Generic object to delete
        /// </param>
        public void Delete<T>(T genericObject) where T : IGenericObject
        {
            if (genericObject == null)
            {
                return;
            }
            GenericEntityDataStorage.Delete(new GenericEntity(genericObject, ApplicationKey));
        }

        /// <summary>
        /// Deletes the given list of generic object
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        /// <param name="genericObjects">
        /// List of generic object to delete
        /// </param>
        public void Delete<T>(IList<T> genericObjects) where T : IGenericObject
        {
            if (genericObjects == null || genericObjects.Count == 0)
            {
                return;
            }
            IList<GenericEntity> genericEntities = new List<GenericEntity>();
            foreach (T genericObject in genericObjects)
            {
                if (genericObject != null)
                {
                    genericEntities.Add(new GenericEntity(genericObject, ApplicationKey));
                }
            }
            GenericEntityDataStorage.Delete(genericEntities);
        }

        /// <summary>
        /// Deletes all generic objects of the generic object type
        /// </summary>
        /// <typeparam name="T">
        /// Generic object type
        /// </typeparam>
        public void Delete<T>() where T : IGenericObject
        {
            GenericEntityDataStorage.Delete(ApplicationKey, typeof(T).FullName);
        }

        /// <summary>
        /// Deletes all generic objects
        /// </summary>
        public void DeleteAll()
        {
            GenericEntityDataStorage.Delete(ApplicationKey);
        }

    }

}
